package com.etl.dataflow.executor.element;

import cn.hutool.json.JSONObject;
import com.etl.dataflow.common.util.ClassSize;

import java.util.*;

/**
 * @author dx
 * @since 2022/4/14
 */
public class DefaultRow extends Row {

    private Column<?>[] data = new Column[DEFAULT_SIZE];
    /**
     * 列名与列下标的映射
     */
    private final Map<String, Integer> nameToIndexMap = new HashMap<>(DEFAULT_SIZE);

    private int size = 0;
    private int contentByteSize = 0;

    // Row本身需要的内存: 成员属性占用空间 + 数组占用空间
    private int memorySize = ClassSize.DEFAULT_ROW_HEAD_SIZE + (DEFAULT_SIZE * ClassSize.COLUMN_HEAD);

    /**
     * 重新计算内存占用
     */
    private void calcMemorySize() {
        // Row本身需要的内存: 成员属性占用空间 + 数组占用空间
        memorySize = ClassSize.DEFAULT_ROW_HEAD_SIZE + (this.data.length * ClassSize.COLUMN_HEAD);
        contentByteSize = 0;
        for (Column<?> column : this.data) {
            incrMemorySize(column);
        }
    }

    private void incrMemorySize(Column<?> column) {
        if (column != null) {
            contentByteSize += column.getByteSize();
            // 内存的占用是column对象的头 再加实际大小
            memorySize += ClassSize.COLUMN_HEAD + column.getByteSize();
        }
    }

    @Override
    public Column<?> getColumn(int i) {
        i--;
        if (i >= size || i < 0) {
            throw new IndexOutOfBoundsException();
        }
        return this.data[i];
    }

    @Override
    public void setColumn(int i, Column<?> column) {
        if (i < 1) {
            throw new IndexOutOfBoundsException();
        }
        i--;
        boolean needCalcMemory = (i >= this.size);
        ensureCapacity(i);
        this.data[i] = column;
        this.size = Math.max(this.size, i + 1);
        // 扩容后 重新计算内存占用
        if (needCalcMemory) {
            this.calcMemorySize();
        }
        // 未扩容 将列的内存占用累加
        else {
            this.incrMemorySize(column);
        }
        // 维护列名和下标的映射关系
        nameToIndexMap.put(column.getName(), i + 1);
    }

    @Override
    public int getColumnNumber() {
        return this.size;
    }

    private void ensureCapacity(int i) {
        if (i <= this.data.length - 1) {
            return;
        }
        int oldCapacity = this.data.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        Column<?>[] columns = new Column[newCapacity];
        System.arraycopy(this.data, 0, columns, 0, this.size);
        this.data = columns;
    }

    @Override
    public String toString() {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < this.size; i++) {
            list.add(this.data[i] == null ? null : this.data[i].asString());
        }
        return Arrays.toString(list.toArray());
    }

    @Override
    public JSONObject toJson() {
        return null;
    }

    @Override
    public int getContentByteSize() {
        return this.contentByteSize;
    }

    @Override
    public int getMemorySize() {
        return this.memorySize;
    }

    @Override
    public Column<?> getColumnByName(String name) {
        Integer index = this.nameToIndexMap.get(name);
        return index == null ? null : this.getColumn(index);
    }
}
